﻿using Nemerle.Collections;
using Nemerle.Peg;
using Nemerle.Text;
using Nemerle.Utility;

using System;
using System.Collections.Generic;
using System.Linq;

namespace ExprParser
{
  [PegGrammar(
    start,
    grammar {
      any                     = ['\u0000'..'\uFFFF'];
      start : PExpr           = expr !any;
      //semicolon : NToken      = ";"s;
      //semicolonOpt            = (";"s)?;

      #region Line terminators

      newLineCharacter = '\n'
                        / '\r'
                        / '\u2028'    /*  line separator       */
                        / '\u2029';   /*  paragraph separator  */
      newLine   = "\r\n" / newLineCharacter;

      #endregion

      #region White space

      whitespace = [Zs]
                / '\t'
                / '\v'        /*  vertial tab          */
                / '\f';       /*  form feed            */

      #endregion

      #region Spacer

      space = whitespace / newLine;

      [InlineAllSubrules]
      s : void = space*;                      /* optional spacer          */

      #endregion

      inentStartChar = ['A'..'Z', 'a'..'z'] / '_';
      identPartChar  = inentStartChar / ['0'..'9'];
      identCars      = inentStartChar identPartChar*;
      operatorChar = '+' / '-' / '\\' / '*' / '.' / '!' / '~' / '<' / '>' / '=' / '&' / '|' / '^' / '%' / '#' / '$' / '/';
      openBracket     : NToken = "("s;
      closeBracket    : NToken = ")"s;

      ifExpr          : PExpr       = "if"s openBracket expr closeBracket expr "else"s expr;

      num             : PExpr       = ['0'..'9']+ s;
      ident           : PExpr       = identCars s;
      operatorRef     : Token       = operatorChar+ s;
      simpleExpr      : PExpr       = ifExpr / num / parenthesesExpr / ident;
      parenthesesExpr : PExpr       = openBracket expr closeBracket;
      expr            : PExpr       = simpleExpr? (operatorRef+ simpleExpr)* operatorRef*;
    }
  )]
  class ExprParser
  {
    operatorRef(op : NToken) : Token
    {
      Token.Operator(GetText(op))
    }

    ident(text : NToken) : PExpr
    {
      PExpr.Ident(GetText(text))
    }

    num(text : NToken) : PExpr
    {
      PExpr.Num(int.Parse(GetText(text)))
    }

    parenthesesExpr(_ : NToken, expr : PExpr, _ : NToken) : PExpr
    {
      expr
    }

    expr(exprOpt : option[PExpr], instrs : List[List[Token] * PExpr], tailOps : List[Token]) : PExpr
    {
      def res = List();

      when (exprOpt is Some(expr))
        res.Add(Token.Expr(expr));

      foreach ((operators, expr) in instrs)
      {
        res.AddRange(operators);
        res.Add(Token.Expr(expr));
      }

      res.AddRange(tailOps);

      Pratt.Parse(res)
    }

    ifExpr(_ : NToken, _ : NToken, cond : PExpr, _ : NToken, e1 : PExpr, _ : NToken, e2 : PExpr) : PExpr
    {
      PExpr.If(cond, e1, e2)
    }
  }
}
